import { dev } from '@mintlify/previewing';

import { cli } from '../src/cli.js';

vi.mock('@mintlify/previewing', () => ({ dev: vi.fn() }));

vi.mock('readline/promises', () => {
  return {
    createInterface: () => {
      return {
        close: () => undefined,
        question: (question: string) => {
          console.log(question);
          return 'y';
        },
      };
    },
  };
});

const allowedPorts = [3000, 5002];

vi.mock('detect-port', () => ({
  default: (port: number) => (allowedPorts.includes(port) ? port : port + 1),
}));

describe('cli', () => {
  let originalArgv: string[];

  beforeEach(() => {
    // Remove all cached modules, otherwise the same results are shown in subsequent tests.
    vi.resetModules();

    // Keep track of original process arguments.
    originalArgv = process.argv;
  });

  afterEach(() => {
    vi.resetAllMocks();

    // Set process arguments back to the original value.
    process.argv = originalArgv;
  });

  it('should run dev command', async () => {
    await runCommand('dev');

    expect(dev).toHaveBeenCalledWith(expect.objectContaining({ port: 3000 }));
  });

  it('port 5000 and 5001 should be taken and 5002 should be accepted by the user and available.', async () => {
    const consoleSpy = vi.spyOn(console, 'log');

    await runCommand('dev', '--port=5000');

    expect(consoleSpy).toHaveBeenCalledTimes(2);
    expect(consoleSpy).toHaveBeenCalledWith(`Port 5000 is already in use. Trying 5001 instead.`);
    expect(consoleSpy).toHaveBeenCalledWith(`Port 5001 is already in use. Trying 5002 instead.`);
    expect(dev).toHaveBeenCalledWith(expect.objectContaining({ port: 5002 }));
  });

  it('fails after the 10th used port', async () => {
    const consoleSpy = vi.spyOn(console, 'log');

    await runCommand('dev', '--port=8000');

    expect(consoleSpy).toHaveBeenCalledTimes(9);
    expect(consoleSpy).toHaveBeenLastCalledWith(
      `Port 8008 is already in use. Trying 8009 instead.`
    );
  });
});

/**
 * Programmatically set arguments and execute the CLI script
 *
 * @param {...string} args - Additional command arguments.
 */
async function runCommand(...args: string[]) {
  process.argv = ['node', 'cli.js', ...args];
  return cli();
}
